package zhupf.test.designPattern.singleton;

/**
 * 双重检查方式
 */
public class SingletonTest06 {
    public static void main(String[] args) {
        for (int i = 0 ;i<10000;i++){
            new Thread(()->{
                Singleton06.getInstance();
            },String.valueOf(i)).start();
        }
    }
}

/**
 *  私有化构造
 *  对外提供公共方法
 *  对 对外提供获取实例方法进行加锁
 *
 *   一般双重检查已经能够满足懒加载咋多线程下的使用，但是，并不是绝对的安全
 *    因为synchronized 并不能防止指令重排
 *    instance = new Singleton06() 会发生指令重排
 *    底层分为三步：
 *    1、memory = allocate(); 1、分配对象内存空间
 *    2、instance(memory); 2、初始化对象
 *    3、 instance = memory; 3、设置instance 指向刚分配的内存地址，只是instance ！= null；
 *
 *    因为步骤2 和步骤3 不存在数据的依赖关系，而且重排前和重排后在单线程的结果没有影响，所以jvm对于这个重排是允许的
 *    重拍后的顺序为：1、3、2  这样虽然 instance = new Singleton06(); 分配了内存空间，也将内存地址赋值给了变量，但是还没有初始化对象
 *    所以此时堆空间中 的 Singleton06 实例依然为null，其他线程依然判断为null，导致线程不安全
 *    volatile可以防止指令重排
 */
class Singleton06{
    private static volatile Singleton06 instance;

    private Singleton06(){
        System.out.println(" 我是单例模式构造函数 ");
    }

    public static  Singleton06 getInstance(){
        if(instance == null){
            synchronized(Singleton06.class){
                if(instance == null){
                    instance = new Singleton06();
                }
            }
        }
        return instance;
    }
}
